package userHelp

import (
	"common/config"
	"common/helper"
	myredis "common/redis"
	"context"
	"fmt"
	"math"
	"math/rand"
	"strconv"
	"time"
)

/*
初始化抽奖基础信息
*/

type LotteryBaseInfo struct {
	//固定
	SerialNo       string `json:"SerialNo" db:"SerialNo" cbor:"SerialNo"`                   //抽奖期号
	BeginTimeStamp int64  `json:"BeginTimeStamp" db:"BeginTimeStamp" cbor:"BeginTimeStamp"` //抽奖开始时间(时间戳)
	EndTimeStamp   int64  `json:"EndTimeStamp" db:"EndTimeStamp" cbor:"EndTimeStamp"`       //抽奖结束时间(时间戳)
	RealUserAward  int    `json:"LotteryAward" db:"LotteryAward" cbor:"LotteryAward"`       //真实玩家计入奖池充值总额
	VirtualAward   int    `json:"VirtualAward" db:"VirtualAward" cbor:"VirtualAward"`       //真实玩家计入奖池充值总额

	//变化
	JoinInPerson int `json:"JoinInPerson" db:"total_participants" cbor:"JoinInPerson"` //总人数(虚拟+真实)
	TotalShare   int `json:"TotalShare" db:"total_shares" cbor:"TotalShare"`           //总份额数(虚拟+真实)

	//变化
	JoinInRealPerson int `json:"JoinInRealPerson" db:"real_participants" cbor:"JoinInRealPerson"` //真实人数
	TotalRealShare   int `json:"TotalRealShare" db:"real_shares" cbor:"TotalRealShare"`           //虚拟人数
}

var G_LotteryBaseInfo LotteryBaseInfo

var G_RecentJoinPerson [3]string = [3]string{"", "", ""}

// 读写redis
var RedisLotteryKey string = "Redsi-Lottery"

var G_NormalizedWeight [24]float64

// 定义24小时权重分布
func generateWeights() []float64 {
	weights := make([]float64, 24)
	for i := 0; i < 24; i++ {
		// 使用两个正弦波模拟双峰值：下午2点（14时）和晚上9点（21时）
		weight1 := math.Sin(float64(i-14)/6*math.Pi) + 1 // 下午2点峰值
		weight2 := math.Sin(float64(i-21)/6*math.Pi) + 1 // 晚上9点峰值
		weights[i] = weight1 + weight2
	}

	total := 0.0
	for _, w := range weights {
		total += w
	}

	for i, w := range weights {
		G_NormalizedWeight[i] = w / total
	}

	return weights
}

// 获取当前时间的累加值（以凌晨3点为起始时间）
func CalculateCumulativeValue(baseValue float64) float64 {

	// 偏移权重，以凌晨3点为起始时间
	offset := 3 // 凌晨3点

	// 获取当前时间
	now := time.Now()
	currentHour := now.Hour()
	currentMinute := now.Minute()

	// 将当前时间映射到新时间轴
	newHour := (currentHour - offset + 24) % 24

	// 计算当前时间之前的累加值
	cumulativeWeight := 0.0
	for hour := 0; hour < newHour; hour++ {
		cumulativeWeight += G_NormalizedWeight[hour]
	}
	// 加上当前小时内的部分权重
	cumulativeWeight += G_NormalizedWeight[newHour] * (float64(currentMinute) / 60)

	// 计算累加值
	cumulativeValue := baseValue * cumulativeWeight

	if cumulativeValue > baseValue {
		cumulativeValue = baseValue
	}

	helper.InfoLog("CalculateCumulativeValue:%d", int(cumulativeValue))
	return cumulativeValue
}

func AddRecentJoinPserson(userName string) {
	if G_RecentJoinPerson[0] == "" {
		G_RecentJoinPerson[0] = userName
		return
	}

	if G_RecentJoinPerson[1] == "" {
		G_RecentJoinPerson[1] = userName
		return
	}

	if G_RecentJoinPerson[2] == "" {
		G_RecentJoinPerson[2] = userName
		return
	}

	G_RecentJoinPerson[0] = G_RecentJoinPerson[1]
	G_RecentJoinPerson[1] = G_RecentJoinPerson[2]
	G_RecentJoinPerson[2] = userName
}
func ResetLottery() {
	G_LotteryBaseInfo.SerialNo = time.Now().Format("20060102")

	now := time.Now()
	startOfDay := time.Date(now.Year(), now.Month(), now.Day(), config.G_LotteryCfg.OpenHour, 0, 0, 0, now.Location())
	G_LotteryBaseInfo.BeginTimeStamp = startOfDay.Unix()
	G_LotteryBaseInfo.EndTimeStamp = startOfDay.Unix() + 24*3600 - 1

	G_LotteryBaseInfo.RealUserAward = 0
	G_LotteryBaseInfo.VirtualAward = 0
	G_LotteryBaseInfo.JoinInPerson = 0
	G_LotteryBaseInfo.TotalShare = 0

	G_LotteryBaseInfo.JoinInRealPerson = 0
	G_LotteryBaseInfo.TotalRealShare = 0
	LotterySnapslot()
}
func ReadLotteryFromRedis() bool {
	retValue, err := myredis.GetRedisInstance().HGet(context.Background(), RedisLotteryKey, "SerialNo").Result()
	if err != nil {
		return false
	}

	if retValue == "" {
		return false
	}

	G_LotteryBaseInfo.SerialNo = retValue

	retValue, err = myredis.GetRedisInstance().HGet(context.Background(), RedisLotteryKey, "BeginTimeStamp").Result()
	G_LotteryBaseInfo.BeginTimeStamp, _ = strconv.ParseInt(retValue, 10, 64)

	retValue, err = myredis.GetRedisInstance().HGet(context.Background(), RedisLotteryKey, "EndTimeStamp").Result()
	G_LotteryBaseInfo.EndTimeStamp, _ = strconv.ParseInt(retValue, 10, 64)

	retValue, err = myredis.GetRedisInstance().HGet(context.Background(), RedisLotteryKey, "RealUserAward").Result()
	G_LotteryBaseInfo.RealUserAward, _ = strconv.Atoi(retValue)

	retValue, err = myredis.GetRedisInstance().HGet(context.Background(), RedisLotteryKey, "VirtualAward").Result()
	G_LotteryBaseInfo.VirtualAward, _ = strconv.Atoi(retValue)

	retValue, err = myredis.GetRedisInstance().HGet(context.Background(), RedisLotteryKey, "JoinInPerson").Result()
	G_LotteryBaseInfo.JoinInPerson, _ = strconv.Atoi(retValue)

	retValue, err = myredis.GetRedisInstance().HGet(context.Background(), RedisLotteryKey, "TotalShare").Result()
	G_LotteryBaseInfo.TotalShare, _ = strconv.Atoi(retValue)

	retValue, err = myredis.GetRedisInstance().HGet(context.Background(), RedisLotteryKey, "JoinInRealPerson").Result()
	G_LotteryBaseInfo.JoinInRealPerson, _ = strconv.Atoi(retValue)

	retValue, err = myredis.GetRedisInstance().HGet(context.Background(), RedisLotteryKey, "TotalRealShare").Result()
	G_LotteryBaseInfo.TotalRealShare, _ = strconv.Atoi(retValue)

	for i := 0; i < len(G_RecentJoinPerson); i++ {
		Field := fmt.Sprintf("JoinPerson-%d", i)
		retValue, err = myredis.GetRedisInstance().HGet(context.Background(), RedisLotteryKey, Field).Result()
		G_RecentJoinPerson[i] = retValue
	}

	return true
}
func WriteLotteryToRedis() {
	myredis.GetRedisInstance().HSet(context.Background(), RedisLotteryKey, "SerialNo", G_LotteryBaseInfo.SerialNo)
	myredis.GetRedisInstance().HSet(context.Background(), RedisLotteryKey, "BeginTimeStamp", G_LotteryBaseInfo.BeginTimeStamp)
	myredis.GetRedisInstance().HSet(context.Background(), RedisLotteryKey, "EndTimeStamp", G_LotteryBaseInfo.EndTimeStamp)
	myredis.GetRedisInstance().HSet(context.Background(), RedisLotteryKey, "RealUserAward", G_LotteryBaseInfo.RealUserAward)
	myredis.GetRedisInstance().HSet(context.Background(), RedisLotteryKey, "VirtualAward", G_LotteryBaseInfo.VirtualAward)
	myredis.GetRedisInstance().HSet(context.Background(), RedisLotteryKey, "JoinInPerson", G_LotteryBaseInfo.JoinInPerson)
	myredis.GetRedisInstance().HSet(context.Background(), RedisLotteryKey, "TotalShare", G_LotteryBaseInfo.TotalShare)
	myredis.GetRedisInstance().HSet(context.Background(), RedisLotteryKey, "JoinInRealPerson", G_LotteryBaseInfo.JoinInRealPerson)
	myredis.GetRedisInstance().HSet(context.Background(), RedisLotteryKey, "TotalRealShare", G_LotteryBaseInfo.TotalRealShare)
	myredis.GetRedisInstance().Expire(context.Background(), RedisLotteryKey, time.Duration(1000)*time.Hour)

	UpdateSnapSlot()
	for i, str := range G_RecentJoinPerson {
		if str != "" {
			Field := fmt.Sprintf("JoinPerson-%d", i)
			myredis.GetRedisInstance().HSet(context.Background(), RedisLotteryKey, Field, str)
		}
	}
}
func TimerAddFakeDeposit() {
	for {
		//time.Sleep(5 * time.Minute) // 每5分钟处理一次
		time.Sleep(60 * time.Second) // 每5分钟处理一次

		randAddDeposit := NextRechargeAmount()
		G_LotteryBaseInfo.TotalShare += randAddDeposit / config.G_LotteryCfg.MinRechargeAmount
		G_LotteryBaseInfo.VirtualAward += randAddDeposit

		//加人数
		r := rand.New(rand.NewSource(time.Now().UnixNano()))
		rv := r.Intn(100) + 1

		helper.InfoLog("TimerAddFakeDeposit:%d---%d----%d", randAddDeposit, config.G_LotteryCfg.MinRechargeAmount, rv)
		if randAddDeposit >= config.G_LotteryCfg.MinRechargeAmount {
			if G_LotteryBaseInfo.JoinInPerson == 0 {
				G_LotteryBaseInfo.JoinInPerson += 1
			} else {
				if rv >= 35 {
					G_LotteryBaseInfo.JoinInPerson += 1
					SqlSelect := "SELECT username FROM tbl_member_nickname ORDER BY RAND() LIMIT 1;"

					var selectItem string = ""
					err := myredis.GetMasterDBInstance().Get(&selectItem, SqlSelect)

					if err == nil && selectItem != "" {
						userName := fmt.Sprintf("%s comprou %d cotas", selectItem, randAddDeposit/config.G_LotteryCfg.MinRechargeAmount)
						AddRecentJoinPserson(userName)
					}
				}
			}
		}

		if randAddDeposit > 0 {
			WriteLotteryToRedis()
		}
	}
}
func AddLottery(uid string, depositAmount float64) {
	lotteryDeposit := GetLotteryDeposit(uid)
	LotteryCharge := float64(config.G_LotteryCfg.MinRechargeAmount)
	if lotteryDeposit < LotteryCharge && (lotteryDeposit+depositAmount) >= LotteryCharge {
		AddJoinInRealPerson(1)
		var lotteryShare int = (int)(lotteryDeposit+depositAmount) / config.G_LotteryCfg.MinRechargeAmount
		userName := fmt.Sprintf("%s comprou %d cotas", GetMemberName(uid), lotteryShare)
		AddRecentJoinPserson(userName)
	}

	curShare := (int)(lotteryDeposit / LotteryCharge)
	newShare := (int)((lotteryDeposit + depositAmount) / LotteryCharge)

	if newShare > curShare {
		AddTotalRealShare(newShare - curShare)
	}

	G_LotteryBaseInfo.RealUserAward += int(depositAmount)
}

// 实际分发的奖池
func GetRealPoolAward() int {
	var PoolRealAward int = 0
	DiscountAmount := config.G_LotteryCfg.BaseAward * config.G_LotteryCfg.DiscountRate / 100
	DepositAmount := G_LotteryBaseInfo.RealUserAward * config.G_LotteryCfg.DeductionRate / 100
	GreeyAmount := DepositAmount * config.G_LotteryCfg.GreedyRate / 100

	PoolRealAward = DiscountAmount + DepositAmount - GreeyAmount
	if PoolRealAward < 0 {
		PoolRealAward = 0
	}
	return PoolRealAward
}

func GetH5TotalAward() int {
	var PoolTotalAward int = 0
	CumulativeValue := int(CalculateCumulativeValue(float64(config.G_LotteryCfg.BaseAward)))

	if G_LotteryBaseInfo.TotalShare == 0 {
		CumulativeValue = 0
	}

	PoolTotalAward = G_LotteryBaseInfo.RealUserAward*config.G_LotteryCfg.DeductionRate/100 +
		G_LotteryBaseInfo.VirtualAward*config.G_LotteryCfg.DeductionRate/100 + CumulativeValue

	return PoolTotalAward
}
func GetPoolVirtualAward() int {
	var PoolTotalAward int = 0
	PoolTotalAward = G_LotteryBaseInfo.RealUserAward*config.G_LotteryCfg.DeductionRate/100 +
		G_LotteryBaseInfo.VirtualAward*config.G_LotteryCfg.DeductionRate/100
	return PoolTotalAward
}

func GetPoolRealAward() int {
	var PoolTotalAward int = 0
	PoolTotalAward = G_LotteryBaseInfo.RealUserAward * config.G_LotteryCfg.DeductionRate / 100
	return PoolTotalAward
}
func GetPoolTotalAward() int {
	var PoolTotalAward int = 0
	PoolTotalAward = G_LotteryBaseInfo.RealUserAward*config.G_LotteryCfg.DeductionRate/100 +
		G_LotteryBaseInfo.VirtualAward*config.G_LotteryCfg.DeductionRate/100 + config.G_LotteryCfg.BaseAward
	return PoolTotalAward
}
func AddTotalRealShare(inc int) {
	G_LotteryBaseInfo.TotalRealShare += inc
}

func AddJoinInRealPerson(inc int) {
	G_LotteryBaseInfo.JoinInPerson += inc
	G_LotteryBaseInfo.JoinInRealPerson += inc
}

func InitLottery() {
	generateWeights()
	if ReadLotteryFromRedis() {
		return
	}

	now := time.Now()

	if now.Hour() <= config.G_LotteryCfg.OpenHour {
		yesterday := now.AddDate(0, 0, -1)
		G_LotteryBaseInfo.SerialNo = yesterday.Format("20060102")
		beginTime := time.Date(yesterday.Year(), yesterday.Month(), yesterday.Day(), config.G_LotteryCfg.OpenHour, 0, 0, 0, now.Location())
		G_LotteryBaseInfo.BeginTimeStamp = beginTime.Unix()
		G_LotteryBaseInfo.EndTimeStamp = beginTime.Unix() + 24*3600 - 1
	} else {
		G_LotteryBaseInfo.SerialNo = time.Now().Format("20060102")
		beginTime := time.Date(now.Year(), now.Month(), now.Day(), config.G_LotteryCfg.OpenHour, 0, 0, 0, now.Location())
		G_LotteryBaseInfo.BeginTimeStamp = beginTime.Unix()
		G_LotteryBaseInfo.EndTimeStamp = beginTime.Unix() + 24*3600 - 1
	}

	G_LotteryBaseInfo.RealUserAward = 0
	G_LotteryBaseInfo.VirtualAward = 0

	//参加人数
	SqlSelect := fmt.Sprintf("Select count(1) from tbl_member_lottery where lottery_deposit >= %d", config.G_LotteryCfg.MinRechargeAmount)
	err := myredis.GetMasterDBInstance().Get(&G_LotteryBaseInfo.JoinInPerson, SqlSelect)
	if err != nil {
		helper.InfoLog("sql error:%s", err.Error())
	}

	SqlSelect = fmt.Sprintf("Select ifnull(sum(FLOOR(lottery_deposit/%d)),0) from tbl_member_lottery", config.G_LotteryCfg.MinRechargeAmount)
	err = myredis.GetMasterDBInstance().Get(&G_LotteryBaseInfo.TotalShare, SqlSelect)
	if err != nil {
		helper.InfoLog("sql error:%s", err.Error())
	}
	LotterySnapslot()
	WriteLotteryToRedis()
}

type Winner struct {
	ID         int
	PlayerName string
	Rank       string
	Prize      float64
	IsReal     bool
}

// PrizeRule 表示一个奖项规则
type PrizeRule struct {
	Rank       string  `json:"rankname" db:"rankname" cbor:"rankname"`
	Percentage float64 `json:"rate" db:"rate" cbor:"rate"`
	Count      int     `json:"count" db:"count" cbor:"count"`
}

type RealPlayer struct {
	ID     int     `json:"uid" db:"uid" cbor:"uid"`
	Name   string  `json:"username" db:"username" cbor:"username"`
	Weight float64 `json:"lottery_deposit" db:"lottery_deposit" cbor:"lottery_deposit"`
}

type lotterySnapshot struct {
	PrizeRules        []PrizeRule `json:"prizeRules"`
	OpenHour          int         `json:"open_hour" db:"open_hour" cbor:"open_hour"`
	Mode              int         `json:"mode" db:"mode" cbor:"mode"`
	DeductionRate     int         `json:"deduction_rate" db:"deduction_rate" cbor:"deduction_rate"`
	BaseAward         int         `json:"base_award" db:"base_award" cbor:"base_award"`
	MinRechargeAmount int         `json:"min_recharge_amount" db:"min_recharge_amount" cbor:"min_recharge_amount"`
	GreedyRate        int         `json:"greedy_rate" db:"greedy_rate" cbor:"greedy_rate"`
	DiscountRate      int         `json:"discount_rate" db:"discount_rate" cbor:"discount_rate"`
}

// 增加假的11条记录
func AddFakeRec() {
	//往lottery_prize 表中加插入数据
	SqlSelect := "SELECT username FROM tbl_member_nickname ORDER BY RAND() LIMIT 11;"

	var selectItem []string
	err := myredis.GetMasterDBInstance().Select(&selectItem, SqlSelect)

	if err != nil {
		helper.SqlSelectErrLog("LotterySpin:%s---%s", SqlSelect, err.Error())
		return
	}

	for i, username := range selectItem {
		//生成唯一ID
		rand.New(rand.NewSource(time.Now().UnixNano()))
		sid := rand.Intn(89999999)
		sid += 10000000

		var level int = 0
		var award int = 0
		if i == 0 {
			level = 1
			award = GetRealPoolAward() * 8 / 10
		} else {
			level = 2
			award = GetRealPoolAward() * 2 / 10 / 10
		}

		level_desp := fmt.Sprintf("%dº prêmio", level)
		sqlInsert := fmt.Sprintf("insert into tbl_rec_lottery(serial_no,uid,username,level,award,created_at,level_desp,is_real) "+
			"VALUES('%s',%d,'%s',%d,%d,%d,'%s',10)", G_LotteryBaseInfo.SerialNo, sid, username,
			level, award, time.Now().Unix(), level_desp)
		myredis.AddLogToRedis(sqlInsert)
	}
}

// 更新当前的数据
func UpdateSnapSlot() {

	pool_amount := int(GetPoolTotalAward())
	SqlUpdate := fmt.Sprintf("update lottery_issue set pool_amount=%d,"+
		"	total_participants=%d,"+
		"	real_participants=%d,"+
		"	total_shares=%d,"+
		"	real_shares=%d,"+
		"	base_award=%d,"+
		"	tax_award=%d,"+
		"	tax_real_award=%d,"+
		"	real_user_deposit=%d,"+
		"	virtual_user_deposit=%d"+
		"	where issue_id='%s';",
		pool_amount,
		G_LotteryBaseInfo.JoinInPerson,
		G_LotteryBaseInfo.JoinInRealPerson,
		G_LotteryBaseInfo.TotalShare,
		G_LotteryBaseInfo.TotalRealShare,
		config.G_LotteryCfg.BaseAward,
		GetPoolVirtualAward(),
		GetPoolRealAward(),
		G_LotteryBaseInfo.RealUserAward,
		G_LotteryBaseInfo.VirtualAward,
		G_LotteryBaseInfo.SerialNo)
	_, err := myredis.GetMasterDBInstance().Exec(SqlUpdate)
	if err != nil {
		helper.SqlErrLog("SnapSlot:%s----%s", SqlUpdate, err.Error())
	}
}

func LotterySnapslot() {
	var prizeRules []PrizeRule
	sqlSelect := fmt.Sprintf("SELECT rankname,rate,count from tbl_cfg_lottery_prize_rule order by count desc ")
	err := myredis.GetMasterDBInstance().Select(&prizeRules, sqlSelect)
	if err != nil {
		//没有游戏规则
		helper.SqlErrLog("AutoLottery prize:%s----%s", sqlSelect, err.Error())
		return
	}

	/*
		SqlDel := fmt.Sprintf("delete from lottery_issue where issue_id='%s';", G_LotteryBaseInfo.SerialNo)
		_, err = myredis.GetMasterDBInstance().Exec(SqlDel)
		if err != nil {
			helper.SqlErrLog("SnapSlot:%s----%s", SqlDel, err.Error())
		}
	*/

	//插入抽奖快照
	var snapslot lotterySnapshot

	snapslot.OpenHour = config.G_LotteryCfg.OpenHour
	snapslot.Mode = config.G_LotteryCfg.Mode
	snapslot.DeductionRate = config.G_LotteryCfg.DeductionRate
	snapslot.BaseAward = config.G_LotteryCfg.BaseAward
	snapslot.MinRechargeAmount = config.G_LotteryCfg.MinRechargeAmount
	snapslot.GreedyRate = config.G_LotteryCfg.GreedyRate
	snapslot.DiscountRate = config.G_LotteryCfg.DiscountRate

	for _, rule := range prizeRules {
		snapslot.PrizeRules = append(snapslot.PrizeRules, rule)
	}

	bytes, err := helper.JsonMarshal(snapslot)
	strSnapslot := string(bytes)

	pool_amount := int(GetPoolTotalAward())
	sqlSnapSlot := fmt.Sprintf("insert into lottery_issue(issue_id,start_time,end_time,pool_amount,"+
		"	total_participants,real_participants,"+
		"	total_shares,real_shares,"+
		"	base_award,tax_award,tax_real_award,"+
		"	snapshot_config,real_user_deposit,virtual_user_deposit,created_at,updated_at) "+
		"	values('%s',%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,'%s',%d,%d,%d,%d)", G_LotteryBaseInfo.SerialNo,
		G_LotteryBaseInfo.BeginTimeStamp, G_LotteryBaseInfo.EndTimeStamp, pool_amount,
		G_LotteryBaseInfo.JoinInPerson, G_LotteryBaseInfo.JoinInRealPerson,
		G_LotteryBaseInfo.TotalShare, G_LotteryBaseInfo.TotalRealShare,
		config.G_LotteryCfg.BaseAward, GetPoolVirtualAward(), GetPoolRealAward(),
		strSnapslot, G_LotteryBaseInfo.RealUserAward, G_LotteryBaseInfo.VirtualAward,
		time.Now().Unix(), time.Now().Unix())
	_, err = myredis.GetMasterDBInstance().Exec(sqlSnapSlot)
	if err != nil {
		helper.SqlErrLog("SnapSlot:%s----%s", sqlSnapSlot, err.Error())
	}
}

// 自动抽奖
func AutoLottery() {

	helper.InfoLog("start AutoLottery")
	//AddFakeRec()
	//加载中奖规则
	config.ReadLotteryCfgFromDB()
	//LotterySnapslot()

	//加载所有充值玩家,
	var allRealPlayers []RealPlayer

	var agentlotteryCount int = 0
	sqlSelect := fmt.Sprintf("SELECT count(1) from tbl_cfg_lottery_agent")
	err := myredis.GetMasterDBInstance().Get(&agentlotteryCount, sqlSelect)
	if err != nil {
		helper.SqlErrLog("allRealPlayers prize:%s----%s", sqlSelect, err.Error())
	}

	if agentlotteryCount == 0 {
		sqlSelect = fmt.Sprintf("SELECT uid,lottery_deposit,username from tbl_member_lottery where lottery_deposit>=%d",
			config.G_LotteryCfg.MinRechargeAmount)
	} else {
		sqlSelect = fmt.Sprintf("select a.uid as uid,a.lottery_deposit as lottery_deposit,a.username as username "+
			"	from tbl_member_lottery as a"+
			"	left JOIN tbl_member_base as b"+
			"	on a.uid=b.uid"+
			"	LEFT JOIN tbl_cfg_lottery_agent as c"+
			"	on b.operator_id=c.operator_id"+
			"	where  a.lottery_deposit>=%d and c.operator_id is not NULL",
			config.G_LotteryCfg.MinRechargeAmount)
	}

	err = myredis.GetMasterDBInstance().Select(&allRealPlayers, sqlSelect)
	if err != nil {
		helper.SqlErrLog("allRealPlayers prize:%s----%s", sqlSelect, err.Error())
	}

	rand.Seed(time.Now().UnixNano())

	totalPool := float64(GetPoolTotalAward())
	maxRealPrize := float64(GetRealPoolAward())
	virtualPool := totalPool - maxRealPrize // 虚拟玩家可用（实际会动态计算）

	helper.InfoLog("AutoLottery:base amount:%0.2f---%0.2f----%0.2f", totalPool, maxRealPrize, virtualPool)

	var winners []Winner
	usedRealPlayers := make(map[int]bool) // 已中奖的真实玩家 ID
	remainingRealBudget := maxRealPrize

	var prizeRules []PrizeRule
	sqlSelect = fmt.Sprintf("SELECT rankname,rate,count from tbl_cfg_lottery_prize_rule order by count desc ")
	err = myredis.GetMasterDBInstance().Select(&prizeRules, sqlSelect)
	if err != nil {
		//没有游戏规则
		helper.SqlErrLog("AutoLottery prize:%s----%s", sqlSelect, err.Error())
		return
	}

	// 按奖项处理
	for _, rule := range prizeRules {
		totalPrize := totalPool * float64(rule.Percentage)
		perWinnerPrize := round(float64(totalPrize/float64(rule.Count)), 2)

		fmt.Printf("🏆 处理【%s】: 总奖金 %.2f 元 (%d 人，每人 %.2f 元)\n",
			rule.Rank, totalPrize, rule.Count, perWinnerPrize)

		// 决定多少名额分配给真实玩家
		realWinnersNeeded := 0
		tempCost := 0.0

		// 贪心尝试：尽可能多安排真实玩家，只要不超过预算
		for i := 0; i < rule.Count; i++ {
			if remainingRealBudget >= perWinnerPrize {
				remainingRealBudget -= perWinnerPrize
				tempCost += perWinnerPrize
				realWinnersNeeded++
			} else {
				break
			}
		}

		// 回退，只扣一次
		remainingRealBudget += tempCost // 恢复

		if realWinnersNeeded > len(allRealPlayers)-len(usedRealPlayers) {
			realWinnersNeeded = len(allRealPlayers) - len(usedRealPlayers)
		}

		// 抽取真实中奖者（按权重，不重复）
		var drawnReals []RealPlayer
		for i := 0; i < realWinnersNeeded && len(usedRealPlayers) < len(allRealPlayers); i++ {
			winner := weightedRandomDrawExcluding(allRealPlayers, usedRealPlayers)
			if winner == nil {
				break
			}
			drawnReals = append(drawnReals, *winner)
			usedRealPlayers[winner.ID] = true
		}

		// 分配真实玩家
		for _, p := range drawnReals {
			winners = append(winners, Winner{
				ID:         p.ID,
				PlayerName: p.Name,
				Rank:       rule.Rank,
				Prize:      perWinnerPrize,
				IsReal:     true,
			})
			remainingRealBudget -= perWinnerPrize
		}

		// 虚拟玩家填补剩余名额
		virtualNeeded := rule.Count - len(drawnReals)

		//往lottery_prize 表中加插入数据
		SqlSelect := fmt.Sprintf("SELECT username FROM tbl_member_nickname ORDER BY RAND() LIMIT %d;", virtualNeeded)

		var selectItem []string
		myredis.GetMasterDBInstance().Select(&selectItem, SqlSelect)

		for i := 0; i < virtualNeeded; i++ {
			//生成唯一ID
			rand.New(rand.NewSource(time.Now().UnixNano()))
			sid := rand.Intn(89999999)
			sid += 10000000

			if i < len(selectItem) {
				winners = append(winners, Winner{
					ID:         sid,
					PlayerName: selectItem[i],
					Rank:       rule.Rank,
					Prize:      perWinnerPrize,
					IsReal:     false,
				})
			} else {
				winners = append(winners, Winner{
					ID:         sid,
					PlayerName: fmt.Sprintf("%d_%d", sid, i+1),
					Rank:       rule.Rank,
					Prize:      perWinnerPrize,
					IsReal:     false,
				})
			}
		}
	}

	// 输出结果
	helper.InfoLog("\n📜 最终中奖名单：")
	var totalDistributed, totalRealPrize float64
	for _, w := range winners {
		totalDistributed += w.Prize
		var IsReal int = 0
		if w.IsReal {
			totalRealPrize += w.Prize
			IsReal = 1

			valUid := fmt.Sprintf("%d", w.ID)
			AddBalanceTransactionRecord(valUid, w.Prize, helper.TransactionLottery, "0", helper.GenId(), 0.00, "", "", 0)
			AddUserWageItem(valUid, w.Prize, helper.TransactionLottery)
			LoadUserToRedis(valUid)
			AddUserBalanceByFloat64(valUid, w.Prize)
		}

		sqlInsert := fmt.Sprintf("insert into tbl_rec_lottery(serial_no,uid,username,award,created_at,level_desp,is_real) "+
			"VALUES('%s',%d,'%s',%0.2f,%d,'%s',%v)", G_LotteryBaseInfo.SerialNo, w.ID,
			w.PlayerName, w.Prize, time.Now().Unix(), w.Rank, IsReal)

		helper.InfoLog(sqlInsert)
		myredis.AddLogToRedis(sqlInsert)
	}

	// 虚拟部分实际使用
	totalVirtualPrize := totalDistributed - totalRealPrize

	// 核对
	helper.InfoLog("\n📊 分配总结\n")
	helper.InfoLog("总奖池: %.2f 元\n", totalPool)
	helper.InfoLog("真实玩家分配: %.2f 元（%d 人）\n", totalRealPrize, countReal(winners))
	helper.InfoLog("虚拟玩家分配: %.2f 元（%d 人）\n", totalVirtualPrize, countVirtual(winners))
	helper.InfoLog("合计分配: %.2f 元\n", totalDistributed)

	//清除玩家tbl_member_lottery中的数据
	sqlUpdate := fmt.Sprintf("update tbl_member_lottery set lottery_deposit=0.0")
	_, err = myredis.GetMasterDBInstance().Exec(sqlUpdate)
	if err != nil {
		helper.SqlErrLog("sqlUpdate:%s----%s", sqlUpdate, err.Error())
	}

	generateWeights()
	//清除在线玩家的数据
}

// weightedRandomDrawExcluding 从未中奖的真实玩家中按权重抽一人
func weightedRandomDrawExcluding(players []RealPlayer, excluded map[int]bool) *RealPlayer {
	var totalWeight float64
	candidates := []RealPlayer{}

	for _, p := range players {
		if !excluded[p.ID] {
			candidates = append(candidates, p)
			totalWeight += p.Weight
		}
	}

	if totalWeight <= 0 || len(candidates) == 0 {
		return nil
	}

	r := rand.Float64() * totalWeight
	var cum float64
	for _, p := range candidates {
		cum += p.Weight
		if r <= cum {
			return &p
		}
	}
	return &candidates[0]
}

func round(x float64, n int) float64 {
	factor := math.Pow(10, float64(n))
	return math.Round(x*factor) / factor
}

func abs(x float64) float64 {
	if x < 0 {
		return -x
	}
	return x
}

func countReal(winners []Winner) int {
	var c int
	for _, w := range winners {
		if w.IsReal {
			c++
		}
	}
	return c
}

func countVirtual(winners []Winner) int {
	var c int
	for _, w := range winners {
		if !w.IsReal {
			c++
		}
	}
	return c
}
